`include "defines.v"
module isu(
  input clk,
  input rst_n,
  input ctrl_isu_flush_i,
  // idu in 
  input                 idu_isu_valid_i,
  output                isu_idu_ready_o,
  input  [`FU_W-1:0]    idu_isu_fu_i   ,
  input  [`OP_W-1:0]    idu_isu_op_i   ,
  input  [4:0]          idu_isu_rs1_i  ,
  input  [4:0]          idu_isu_rs2_i  ,
  input  [4:0]          idu_isu_rd_i   ,
  input                 idu_isu_rfwen_i,
  input  [1:0]          idu_isu_src1_i ,
  input  [1:0]          idu_isu_src2_i ,
  input  [`XLEN-1:0]    idu_isu_imm_i  ,
  input  [`VADDR_W-1:0] idu_isu_pc_i   ,
  input  [31:0]         idu_isu_inst_i ,
  input                 idu_isu_pre_i  ,
  /* common signal*/
  output                isu_exu_valid_o,
  input                 exu_isu_ready_i,
  output [`OP_W-1:0]    isu_exu_op_o   ,
  output [4:0]          isu_exu_rd_o   ,
  output                isu_exu_rfwen_o,
  output [`XLEN-1:0]    isu_exu_op1_o  ,
  output [`XLEN-1:0]    isu_exu_op2_o  ,
  output [`XLEN-1:0]    isu_exu_imm_o  ,
  output [`VADDR_W-1:0] isu_exu_pc_o   ,
  output [31:0]         isu_exu_inst_o ,
  output                isu_exu_pre_o  ,
  /* fu enable signal */
  output                alu_enable_o   ,
  output                aluw_enable_o  ,
  output                bru_enable_o   ,
  output                csr_enable_o   ,
  output                lsu_enable_o   ,
  /* exu hzard */
  input                 exu_isu_wen_i   ,  
  input [4:0]           exu_isu_addr_i  ,  
  input [`XLEN-1:0]     exu_isu_data_i  ,  
  /* wb hzard & wen regfile*/
  input                 wbu_isu_wen_i   ,  
  input [4:0]           wbu_isu_addr_i  ,  
  input [`XLEN-1:0]     wbu_isu_data_i   
  // `ifdef DIFFTEST_MY
  ,output [`XLEN-1:0]   debug_a0
  // `endif
);
reg isuValid;
reg [`FU_W-1:0]    q_idu_isu_fu_i    ;
reg [`OP_W-1:0]    q_idu_isu_op_i    ;
reg [4:0]          q_idu_isu_rs1_i   ;
reg [4:0]          q_idu_isu_rs2_i   ;
reg [4:0]          q_idu_isu_rd_i    ;
reg                q_idu_isu_rfwen_i ;
reg [1:0]          q_idu_isu_src1_i  ;
reg [1:0]          q_idu_isu_src2_i  ;
reg [`XLEN-1:0]    q_idu_isu_imm_i   ;
reg [`VADDR_W-1:0] q_idu_isu_pc_i    ;
reg [31:0]         q_idu_isu_inst_i  ;
reg                q_idu_isu_pre_i   ;
wire idu_isu_hs = idu_isu_valid_i && isu_idu_ready_o;
wire isu_exu_hs = isu_exu_valid_o && exu_isu_ready_i;
assign isu_idu_ready_o = ~isuValid || isu_exu_hs;
assign isu_exu_valid_o = isuValid;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    isuValid <= 1'b0;
  else if(ctrl_isu_flush_i)
    isuValid <= 1'b0;
  else if(isu_idu_ready_o)
    isuValid <= idu_isu_valid_i;

always@(posedge clk)
  if(idu_isu_hs)begin
    q_idu_isu_fu_i    <= idu_isu_fu_i   ;
    q_idu_isu_op_i    <= idu_isu_op_i   ;
    q_idu_isu_rs1_i   <= idu_isu_rs1_i  ;
    q_idu_isu_rs2_i   <= idu_isu_rs2_i  ;
    q_idu_isu_rd_i    <= idu_isu_rd_i   ;
    q_idu_isu_rfwen_i <= idu_isu_rfwen_i;
    q_idu_isu_src1_i  <= idu_isu_src1_i ;
    q_idu_isu_src2_i  <= idu_isu_src2_i ;
    q_idu_isu_imm_i   <= idu_isu_imm_i  ;
    q_idu_isu_pc_i    <= idu_isu_pc_i   ;
    q_idu_isu_inst_i  <= idu_isu_inst_i ;
    q_idu_isu_pre_i   <= idu_isu_pre_i  ;
  end

wire rf_ren1 = q_idu_isu_src1_i == `SRC_RF;
wire rf_ren2 = q_idu_isu_src2_i == `SRC_RF;
wire [`XLEN-1:0] rf_rdata1;
wire [`XLEN-1:0] rf_rdata2;

regfile RF(
  .clk    ( clk            ),
  .rst_n  ( rst_n          ),

  .wen    ( wbu_isu_wen_i   ),
  .waddr  ( wbu_isu_addr_i  ),
  .wdata  ( wbu_isu_data_i  ),

  .ren1   ( rf_ren1         ),
  .raddr1 ( q_idu_isu_rs1_i ),
  .rdata1 ( rf_rdata1       ),

  .ren2   ( rf_ren1         ),
  .raddr2 ( q_idu_isu_rs2_i ),
  .rdata2 ( rf_rdata2       ),
  .debug_a0 (debug_a0)
);

/* ex & wb hzard check and forward */
wire exHzard1 = exu_isu_wen_i && rf_ren1
                && (exu_isu_addr_i != 'd0) && (exu_isu_addr_i == q_idu_isu_rs1_i);
wire wbHzard1 = wbu_isu_wen_i && rf_ren1 
            && (wbu_isu_addr_i != 'd0) && (wbu_isu_addr_i === q_idu_isu_rs1_i);
wire[`XLEN-1:0] hzardData1 = exHzard1 ? exu_isu_data_i :
                (wbHzard1 ? wbu_isu_data_i : rf_rdata1);
wire exHzard2 = exu_isu_wen_i && rf_ren2
                && (exu_isu_addr_i != 'd0) && (exu_isu_addr_i == q_idu_isu_rs2_i);
wire wbHzard2 = wbu_isu_wen_i && rf_ren2 
            && (wbu_isu_addr_i != 'd0) && (wbu_isu_addr_i === q_idu_isu_rs2_i);
wire[`XLEN-1:0] hzardData2 = exHzard2 ? exu_isu_data_i :
                (wbHzard2 ? wbu_isu_data_i : rf_rdata2);


assign isu_exu_op_o    = q_idu_isu_op_i;
assign isu_exu_rd_o    = q_idu_isu_rd_i;
assign isu_exu_rfwen_o = q_idu_isu_rfwen_i;
assign isu_exu_op1_o   = (q_idu_isu_src1_i == `SRC_RF) ? hzardData1 : 
                         (q_idu_isu_src1_i == `SRC_OR) ? q_idu_isu_pc_i : `ZERO;
assign isu_exu_op2_o   = (q_idu_isu_src2_i == `SRC_RF) ? hzardData2 : 
                         (q_idu_isu_src2_i == `SRC_OR) ? q_idu_isu_imm_i : `ZERO;
assign isu_exu_imm_o   = q_idu_isu_imm_i ;
assign isu_exu_pc_o    = q_idu_isu_pc_i  ;
assign isu_exu_inst_o  = q_idu_isu_inst_i;
assign alu_enable_o    = q_idu_isu_fu_i == `FU_ALU ; 
assign aluw_enable_o   = q_idu_isu_fu_i == `FU_ALUW;
assign bru_enable_o    = q_idu_isu_fu_i == `FU_BRU ; 
assign csr_enable_o    = q_idu_isu_fu_i == `FU_CSR ; 
assign lsu_enable_o    = q_idu_isu_fu_i == `FU_LSU ; 
assign isu_exu_pre_o   = q_idu_isu_pre_i;

endmodule 